{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "#-*-coding:utf8-*-\n",
    "\n",
    "\"\"\"\n",
    "author:YJM\n",
    "\n",
    "date:20190420\n",
    "\n",
    "\"\"\"\n",
    "from __future__ import division\n",
    "import os\n",
    "import sys\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import operator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 通过movies.csv获取电影信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_item_info(input_file):\n",
    "    if not os.path.exists(input_file):\n",
    "        return {}\n",
    "    item_info={}\n",
    "    linenum=0\n",
    "    fp = open(input_file)\n",
    "    for line in fp:\n",
    "        if linenum == 0:\n",
    "            linenum += 1\n",
    "            continue\n",
    "        item = line.strip().split(',')\n",
    "        if len(item)<3:\n",
    "            continue\n",
    "        elif len(item) == 3:\n",
    "            itemid,title,genre = item[0],item[1],item[2]\n",
    "        elif len(item)>3:\n",
    "            itemid = item[0]\n",
    "            genre = item[-1]\n",
    "            title = ','.join(item[1:-1])\n",
    "        item_info[itemid]=[title,genre]\n",
    "    fp.closed\n",
    "    return item_info"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 图算法的数据格式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_graph_from_data(input_file):\n",
    "    \"\"\"\n",
    "    Args:\n",
    "        input_file:user item rating file\n",
    "    Return:\n",
    "        a dict:{User A:{itemb:1,itemc:1},itemb:{UserA:1}}\n",
    "    \"\"\"\n",
    "    if not os.path.exists(input_file):\n",
    "        return {}   \n",
    "    graph={}\n",
    "    linenum =0\n",
    "    score_thr=4.0\n",
    "    fp = open(input_file)\n",
    "    for line in fp:\n",
    "        if linenum ==0:\n",
    "            linenum +=1\n",
    "            continue\n",
    "        item = line.strip().split(\",\")\n",
    "        if len(item)<3:\n",
    "            continue\n",
    "        userid,itemid,rating =item[0],\"item_\"+item[1],item[2]\n",
    "        if float(rating)<score_thr:\n",
    "            continue\n",
    "        if userid not in graph:\n",
    "            graph[userid] ={}\n",
    "        graph[userid][itemid]=1\n",
    "        if itemid not in graph:\n",
    "            graph[itemid]={}\n",
    "        graph[itemid][userid] = 1\n",
    "    fp.close()\n",
    "    return graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph=get_graph_from_data(\"../data/ratings15000.csv\")\n",
    "# graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tmp_rank ={point:0 for point in graph}\n",
    "# tmp_rank\n",
    "# for out_point ,out_dict in graph.items():\n",
    "#     print(\"--------------------\")\n",
    "#     for inner_point,value in graph[out_point].items():\n",
    "#         print(tmp_rank[out_point])\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 将personalRank的算法模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def personal_rank(graph,root,alpha,iter_num,recom_num=10):\n",
    "    \"\"\"\n",
    "    Args:\n",
    "        graph:user item graph\n",
    "        root:指定要推荐的用户\n",
    "        alpha：以alpha的概率选择随机游走，以1-alpha的概率回到起点\n",
    "        item_num:迭代轮次\n",
    "        recom_num=10:指定迭代轮次\n",
    "    Return:\n",
    "        a dict :    key :itemid  value: pr\n",
    "    \"\"\"\n",
    "    rank = {}\n",
    "    rank = {point:0 for point in graph}#将除了root顶点以外，其他所有顶点初始化为0,一箭双雕，自动去重\n",
    "    rank[root] = 1#root顶点初始化成1\n",
    "    recom_result={}#输出的数据结构\n",
    "    for iter_index in range(iter_num):\n",
    "        tmp_rank = {}\n",
    "        tmp_rank = {point:0 for point in graph}#该迭代轮次下其余顶点到root顶点的pr值\n",
    "        #如果该顶点不是root顶点,那么所有连接该顶点的顶点的pr值以1/N的概率贡献给这个顶点\n",
    "        for out_point,out_dict in graph.items():\n",
    "            for inner_point,value in graph[out_point].items():\n",
    "#                 如果顶点不是root顶点（公式的上半部分）\n",
    "#              len(out_dict)是出度\n",
    "                tmp_rank[inner_point] +=round(alpha*rank[out_point]/len(out_dict),4)\n",
    "#                公式的下半部分\n",
    "                if inner_point == root:\n",
    "                    tmp_rank[inner_point] +=round(1-alpha,4)\n",
    "#         迭代充分了提前结束迭代\n",
    "        if tmp_rank ==rank:\n",
    "            print(\"out\"+str(iter_index))#查看是否提前结束迭代\n",
    "            break\n",
    "#         如果没有完全迭代充分，就要赋值给rank这个数据结构\n",
    "        rank = tmp_rank\n",
    "    \n",
    "    right_num = 0#定义一个计数器\n",
    "    \n",
    "#     将rank这个结构根据pr值的得分进行排序，并过滤掉User顶点和root顶点已经行为过的item \n",
    "    for zuhe in sorted(rank.items(),key=operator.itemgetter(1),reverse=True):\n",
    "        point,pr_score =zuhe[0],zuhe[1]\n",
    "        if len(point.split('_'))<2:#如果不是item顶点就过滤掉\n",
    "            continue\n",
    "        if point in graph[root]:#如果被root顶点行为过，同样过滤\n",
    "            continue\n",
    "        recom_result[point] = pr_score #结果装载进数据集\n",
    "        right_num += 1\n",
    "        if right_num >recom_num:\n",
    "            break#迭代轮次结束\n",
    "    return recom_result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_one_user_recom():\n",
    "    \"\"\"\n",
    "    give one fix user recom result\n",
    "    \"\"\"\n",
    "    user =\"2\"# A\n",
    "    alpha = 0.8      \n",
    "#     graph = get_graph_from_data(\"../data/log.txt\")\n",
    "    graph =get_graph_from_data(\"../data/ratings15000.csv\")\n",
    "    iter_num = 100  \n",
    "    recom_result=personal_rank(graph,user,alpha,iter_num)\n",
    "    item_info = get_item_info(\"../data/movies.csv\")\n",
    "#     将用户感兴趣的物品打印出来分析结果\n",
    "    for itemid in graph[user]:\n",
    "        pure_itemid = itemid.split(\"_\")[1]\n",
    "        print(item_info[pure_itemid])\n",
    "    print(\"result------------\")    \n",
    "    for itemid in recom_result:\n",
    "        pure_itemid = itemid.split(\"_\")[1]\n",
    "        print(item_info[pure_itemid])\n",
    "        print(recom_result[itemid])    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "out31\n",
      "['Grumpier Old Men (1995)', 'Comedy|Romance']\n",
      "[\"Mr. Holland's Opus (1995)\", 'Drama']\n",
      "['From Dusk Till Dawn (1996)', 'Action|Comedy|Horror|Thriller']\n",
      "['Braveheart (1995)', 'Action|Drama|War']\n",
      "['Star Wars: Episode IV - A New Hope (1977)', 'Action|Adventure|Sci-Fi']\n",
      "['Legends of the Fall (1994)', 'Drama|Romance|War|Western']\n",
      "['Jurassic Park (1993)', 'Action|Adventure|Sci-Fi|Thriller']\n",
      "['Blade Runner (1982)', 'Action|Sci-Fi|Thriller']\n",
      "['Terminator 2: Judgment Day (1991)', 'Action|Sci-Fi']\n",
      "['North by Northwest (1959)', 'Action|Adventure|Mystery|Romance|Thriller']\n",
      "['2001: A Space Odyssey (1968)', 'Adventure|Drama|Sci-Fi']\n",
      "['Star Wars: Episode V - The Empire Strikes Back (1980)', 'Action|Adventure|Sci-Fi']\n",
      "['Star Wars: Episode VI - Return of the Jedi (1983)', 'Action|Adventure|Sci-Fi']\n",
      "['Alien (1979)', 'Horror|Sci-Fi']\n",
      "['\"Femme Nikita, La (Nikita) (1990)\"', 'Action|Crime|Romance|Thriller']\n",
      "['Stand by Me (1986)', 'Adventure|Drama']\n",
      "['Back to the Future (1985)', 'Adventure|Comedy|Sci-Fi']\n",
      "['\"Amityville Horror, The (1979)\"', 'Drama|Horror|Mystery|Thriller']\n",
      "['Star Trek: First Contact (1996)', 'Action|Adventure|Sci-Fi|Thriller']\n",
      "['\"Lost World: Jurassic Park, The (1997)\"', 'Action|Adventure|Sci-Fi|Thriller']\n",
      "['Men in Black (a.k.a. MIB) (1997)', 'Action|Comedy|Sci-Fi']\n",
      "['Boogie Nights (1997)', 'Drama']\n",
      "['Dark City (1998)', 'Adventure|Film-Noir|Sci-Fi|Thriller']\n",
      "['Friday the 13th (1980)', 'Horror|Mystery|Thriller']\n",
      "['\"Fly, The (1958)\"', 'Horror|Mystery|Sci-Fi']\n",
      "['\"Fly, The (1986)\"', 'Drama|Horror|Sci-Fi|Thriller']\n",
      "['From Russia with Love (1963)', 'Action|Adventure|Thriller']\n",
      "['\"Fistful of Dollars, A (Per un pugno di dollari) (1964)\"', 'Action|Western']\n",
      "['\"War Zone, The (1999)\"', 'Drama|Thriller']\n",
      "['Any Given Sunday (1999)', 'Drama']\n",
      "['Grumpy Old Men (1993)', 'Comedy']\n",
      "['Rules of Engagement (2000)', 'Drama|Thriller']\n",
      "['U-571 (2000)', 'Action|Thriller|War']\n",
      "['\"Road Warrior, The (Mad Max 2) (1981)\"', 'Action|Adventure|Sci-Fi']\n",
      "['\"Patriot, The (2000)\"', 'Action|Drama|War']\n",
      "['Hellraiser (1987)', 'Horror']\n",
      "['Return of the Fly (1959)', 'Horror|Sci-Fi']\n",
      "['Voyage to the Bottom of the Sea (1961)', 'Adventure|Sci-Fi']\n",
      "['Fantastic Voyage (1966)', 'Adventure|Sci-Fi']\n",
      "['Abbott and Costello Meet Frankenstein (1948)', 'Comedy|Horror']\n",
      "['\"Creature from the Black Lagoon, The (1954)\"', 'Adventure|Horror|Sci-Fi']\n",
      "['Runaway (1984)', 'Sci-Fi|Thriller']\n",
      "['\"Time Machine, The (1960)\"', 'Action|Adventure|Sci-Fi']\n",
      "result------------\n",
      "['\"Shawshank Redemption, The (1994)\"', 'Crime|Drama']\n",
      "0.06030000000000001\n",
      "['Forrest Gump (1994)', 'Comedy|Drama|Romance|War']\n",
      "0.058800000000000005\n",
      "['Pulp Fiction (1994)', 'Comedy|Crime|Drama|Thriller']\n",
      "0.057800000000000004\n",
      "['Toy Story (1995)', 'Adventure|Animation|Children|Comedy|Fantasy']\n",
      "0.0496\n",
      "['\"Silence of the Lambs, The (1991)\"', 'Crime|Horror|Thriller']\n",
      "0.047999999999999994\n",
      "[\"Schindler's List (1993)\", 'Drama|War']\n",
      "0.0427\n",
      "['Raiders of the Lost Ark (Indiana Jones and the Raiders of the Lost Ark) (1981)', 'Action|Adventure']\n",
      "0.042499999999999996\n",
      "['\"Godfather, The (1972)\"', 'Crime|Drama']\n",
      "0.042300000000000004\n",
      "['\"Fugitive, The (1993)\"', 'Thriller']\n",
      "0.041499999999999995\n",
      "['Seven (a.k.a. Se7en) (1995)', 'Mystery|Thriller']\n",
      "0.041100000000000005\n",
      "['Fargo (1996)', 'Comedy|Crime|Drama|Thriller']\n",
      "0.03909999999999999\n"
     ]
    }
   ],
   "source": [
    "get_one_user_recom()\n",
    "# 推荐结果存盘或者存在kv中"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
